home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland Pascal with Objects 7.0 / PAINT.ZIP / TOOLS.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1992-10-27  |  17.0 KB  |  609 lines

  1. {************************************************}
  2. {                                                }
  3. {   ObjectWindows Paint demo                     }
  4. {   Copyright (c) 1992 by Borland International  }
  5. {                                                }
  6. {************************************************}
  7.  
  8. unit Tools;
  9.  
  10. { This unit supplies the actual painting and drawing tools for the paint
  11.   program. Each tool manipulates the bits in a display context in a
  12.   specialized manner. The behaviour of each tool is defined. The icon and
  13.   cursor associated with a tool is specified when the tool is created, but
  14.   the tool itself does not make use of this information.
  15. }
  16.  
  17. interface
  18.  
  19. uses PaintDef, Rect, Strings, WinTypes, WinProcs;
  20.  
  21. type
  22.   { A Draw Tool is a tool whose action is instigated solely by mouse input.
  23.     The action is always fully performed within a single Mouse Down, Mouse
  24.     Move, Mouse Up cycle.
  25.  
  26.     TDrawTool performs the actions necessary to maintain the drawing
  27.     environment (storing window, display context, etc.) so that each tool need
  28.     only implement those of DrawBegin (called on Mouse Down), DrawTo (called
  29.     on Mouse Move) and DrawEnd (called on MouseUp) that perform actions 
  30.     peculiar to that tool.
  31.   }
  32.   PDrawTool = ^TDrawTool;            { Defined in PaintDef }
  33.   TDrawTool = object(TPaintTool)
  34.     Pen, MemPen: HPen;                    { The pens not in use }
  35.     Brush, MemBrush: HBrush;            { The brushes not in use }
  36.  
  37.     { Mouse responses }
  38.     procedure MouseDown(AWindow: HWnd; X, Y: Integer;
  39.       AState: PState); virtual;
  40.     procedure MouseMove(X, Y: Integer); virtual;
  41.     procedure MouseUp; virtual;
  42.   end;
  43.  
  44.   { A Pen tool draws a freeform line using the currently selected pen color
  45.     and width.
  46.   }
  47.   PPenTool = ^TPenTool;
  48.   TPenTool = object(TDrawTool)
  49.  
  50.     { Actual drawing }
  51.     procedure DrawBegin(X, Y: Integer); virtual;
  52.     procedure DrawTo(X, Y: Integer); virtual;
  53.   end;
  54.  
  55.   { An Eraser tool draws a freeform white line using the currently selected
  56.     pen width.
  57.   }
  58.   PEraserTool = ^TEraserTool;
  59.   TEraserTool = object(TPenTool)
  60.     Eraser, MemEraser: HPen;            { The pens not in use }
  61.  
  62.     { Actual drawing }
  63.     procedure DrawBegin(X, Y: Integer); virtual;
  64.     procedure DrawEnd; virtual;
  65.   end;
  66.  
  67.   { A Fill tool fills an area bounded by the current pen color with the
  68.     current brush color.
  69.   }
  70.   PFillTool = ^TFillTool;
  71.   TFillTool = object(TDrawTool)
  72.  
  73.     { Actual drawing }
  74.     procedure DrawBegin(X, Y: Integer); virtual;
  75.   end;
  76.  
  77.   { A Box tool is a tool that operates on a rectangularly bounded area. These
  78.     are tools whose actual drawing calls involve specifying this bounding
  79.     rectangle, e.g., for drawing a rectangle or oval, OR that perform
  80.     rubberbanding during drawing.
  81.   }
  82.   PBoxTool = ^TBoxTool;
  83.   TBoxTool = object(TDrawTool)
  84.     Filled: Boolean;             { Should the internal area be colored }
  85.     X1, Y1, X2, Y2: Integer;         { The bounding rectangle }
  86.  
  87.     { Creation }
  88.     constructor Init(AState: PState; IconName, CursorName: PChar; 
  89.                      AFilled: Boolean);
  90.  
  91.     { Actual drawing }
  92.     procedure DrawBegin(X, Y: Integer); virtual;
  93.     procedure DrawTo(X, Y: Integer); virtual;
  94.     procedure DrawEnd; virtual;
  95.     procedure DrawObject(aDC: HDC); virtual;
  96.   end;
  97.  
  98.   { A Rect tool is a tool that draws (or manipulates) a rectangle.
  99.   }
  100.   PRectTool = ^TRectTool;
  101.   TRectTool = object(TBoxTool)
  102.  
  103.     { Actual drawing }
  104.     procedure DrawObject(aDC: HDC); virtual;
  105.   end;
  106.  
  107.   { A Select tool selects and maintains a rectangular subset (the current 
  108.     selection) of the image. The selection may serve only to specify this
  109.     subset, or it may actively be manipulated (e.g., by dragging).
  110.     If it is used for dragging a separate bitmap is created that exactly
  111.     contains the portion of the image selected.
  112.   }
  113.   PSelectTool = ^TSelectTool;
  114.   TSelectTool = object(TRectTool)
  115.     SelectionDC: HDC;        { Display context for the current selection }
  116.  
  117.     { Creation }
  118.     constructor Init(AState: PState; IconName, CursorName: PChar;
  119.       AFilled: Boolean);
  120.  
  121.     { Re-initilization }
  122.     procedure Deselect; virtual;
  123.  
  124.     { Actual drawing }
  125.     procedure DrawBegin(X, Y: Integer); virtual;
  126.     procedure DrawTo(X, Y: Integer); virtual;
  127.     procedure DrawEnd; virtual;
  128.     procedure DrawObject(aDC: HDC); virtual;
  129.  
  130.     { Utilities }
  131.     procedure PickUpSelection(aDC: HDC; Left, Top, Width, Height: Integer);
  132.       virtual;
  133.     procedure ReleaseSelection; virtual;
  134.     procedure DropSelection; virtual;
  135.   end;
  136.  
  137.   { An Ellipse tool is a tool that draws an ellipse.
  138.   }
  139.   PEllipseTool = ^TEllipseTool;
  140.   TEllipseTool = object(TBoxTool)
  141.  
  142.     { Actual drawing }
  143.     procedure DrawObject(aDC: HDC); virtual;
  144.   end;
  145.  
  146.   { A Line tool draws a straight line.
  147.   }
  148.   PLineTool = ^TLineTool;
  149.   TLineTool = object(TBoxTool)
  150.     
  151.     { Actual drawing }
  152.     procedure DrawObject(aDC: HDC); virtual;
  153.   end;
  154.  
  155.  
  156. implementation
  157.  
  158. { TDrawTool }
  159.  
  160. { Set up the drawing environment for any drawing tool. Note that the
  161.   display context for the off-screen bitmap has already been set up.
  162.   
  163.   Since shared display contexts are used for the window, they should
  164.   be held as shortly as possible. Hence the display context for the window
  165.   is retrieve on each operation.
  166.  
  167. }
  168. procedure TDrawTool.MouseDown(AWindow: HWnd; X, Y: Integer; AState: PState);
  169. begin
  170.   { Set up the window and state }
  171.   Window := AWindow;
  172.   State := AState;
  173.  
  174.   { Direct all mouse input to Window }
  175.   SetCapture(Window);
  176.  
  177.   { Create the actual pens and brushes to be used }
  178.   Pen := CreatePen(ps_Solid, State^.PenSize, State^.PenColor); 
  179.   MemPen := CreatePen(ps_Solid, State^.PenSize, State^.PenColor); 
  180.   Brush := CreateSolidBrush(State^.BrushColor);
  181.   MemBrush := CreateSolidBrush(State^.BrushColor);
  182.  
  183.   { Set up the display contexts }
  184.   DC := GetDC(Window);        
  185.   SelectObject(DC, Pen);
  186.   SelectObject(State^.MemDC, MemPen);
  187.   SelectObject(DC, Brush);
  188.   SelectObject(State^.MemDC, MemBrush);
  189.  
  190.   DrawBegin(X, Y);          { Tell the tool to start drawing }
  191. end;
  192.  
  193. procedure TDrawTool.MouseMove(X, Y: Integer);
  194. begin
  195.   DrawTo(X, Y);              { Tell the tool to do its draw thing }
  196. end;
  197.  
  198. procedure TDrawTool.MouseUp;
  199. begin
  200.   DrawEnd;              { Tell the tool to stop drawing }
  201.  
  202.   { Clean up }
  203.   { Reset mouse input }
  204.   ReleaseCapture;
  205.  
  206.   { Restore display contexts }
  207.   SelectObject(DC, GetStockObject(Black_Pen));
  208.   SelectObject(State^.MemDC, GetStockObject(Black_Pen));
  209.   SelectObject(DC, GetStockObject(White_Brush));
  210.   SelectObject(State^.MemDC, GetStockObject(White_Brush));
  211.   
  212.   { Delete the created objects }
  213.   DeleteObject(Pen);
  214.   DeleteObject(MemPen);
  215.   DeleteObject(Brush);
  216.   DeleteObject(MemBrush);
  217.  
  218.   ReleaseDC(Window, DC);
  219. end;
  220.  
  221. { TPenTool }
  222.  
  223. { Actual drawing }
  224. procedure TPenTool.DrawBegin(X, Y: Integer);
  225. begin
  226.   MoveTo(DC, X, Y);                { Move the pen position }
  227.   MoveTo(State^.MemDC, X+State^.Offset.X, Y+State^.Offset.Y);    { Echo }
  228.   DrawTo(X, Y);                    { Draw the initial pixel(s) }
  229. end;
  230.  
  231. procedure TPenTool.DrawTo(X, Y: Integer);
  232. begin
  233.   LineTo(DC, X, Y);                { Draw a line from the pen position }
  234.   LineTo(State^.MemDC, X+State^.Offset.X, Y+State^.Offset.Y);    { Echo }
  235. end;
  236.  
  237. { TEraserTool }
  238.  
  239. { Actual drawing }
  240. procedure TEraserTool.DrawBegin(X, Y: Integer);
  241. begin
  242.   { Create an erasing pen and reset the display context }
  243.   Eraser := CreatePen(ps_Solid, State^.PenSize, $FFFFFF);
  244.   MemEraser := CreatePen(ps_Solid, State^.PenSize, $FFFFFF);
  245.   SelectObject(DC, Eraser);
  246.   SelectObject(State^.MemDC, MemEraser);
  247.  
  248.   SelectObject(DC, GetStockObject(White_Brush));
  249.   SelectObject(State^.MemDC, GetStockObject(White_Brush));
  250.  
  251.   TPenTool.DrawBegin(X, Y);    { Start drawing }
  252. end;
  253.  
  254. procedure TEraserTool.DrawEnd;
  255. begin
  256.   { Clean up }
  257.   SelectObject(DC, Pen);
  258.   SelectObject(State^.MemDC, MemPen);
  259.   DeleteObject(Eraser);
  260.   DeleteObject(MemEraser);
  261. end;
  262.  
  263. { TFillTool }
  264.  
  265. procedure TFillTool.DrawBegin(X, Y: Integer);
  266. var
  267.  PixelColor: LongInt;
  268. begin
  269.  PixelColor := GetPixel(State^.MemDC, X, Y);
  270.  
  271.  { Change the surface under X, Y to the background color }
  272.  ExtFloodFill(DC, X, Y, PixelColor, FloodFillSurface);
  273.  ExtFloodFill(State^.MemDC, X, Y, PixelColor, FloodFillSurface);
  274. end;
  275.  
  276. { TBoxTool }
  277.  
  278. { Creation }
  279. constructor TBoxTool.Init(AState: PState; IconName, CursorName:
  280.   PChar; AFilled: Boolean);
  281. begin
  282.   TDrawTool.Init(AState, IconName, CursorName);
  283.   Filled := AFilled;        { Record whether tool operates on outline }
  284.                                 { or outline and bounded area }
  285. end;
  286.  
  287. { Actual drawing }
  288. { During the drawing a BoxTool rubberbands a black outline of the final 
  289.   object on the screen by alternately erasing and redrawing the outline. }
  290. procedure TBoxTool.DrawBegin(X, Y: Integer);
  291. begin
  292.   X1 := X;            { Initially the rectangle is a single pixel }
  293.   Y1 := Y;
  294.   X2 := X;
  295.   Y2 := Y;
  296.  
  297.   { Set up the display context to draw a black outline during drawing }
  298.   SelectObject(DC, GetStockObject(Black_Pen));
  299.   SelectObject(DC, GetStockObject(Null_Brush));
  300.  
  301.   { Invert pixels under the pen }
  302.   SetROP2(DC, r2_Not);
  303.  
  304.   { Draw the initial outline }
  305.   DrawObject(DC);
  306. end;
  307.  
  308. procedure TBoxTool.DrawTo(X, Y: Integer);
  309. begin
  310.   { Draw over the outline last drawn. Since the pen inverts pixels and is
  311.     black this will erase the last outline. }
  312.   DrawObject(DC);
  313.  
  314.   { Update the rectangle to be operated on }
  315.   X2 := X;    
  316.   Y2 := Y;
  317.  
  318.   { Draw the new outline }
  319.   DrawObject(DC);
  320. end;
  321.  
  322. procedure TBoxTool.DrawEnd;
  323. begin
  324.   { Erase the last outline drawn }
  325.   DrawObject(DC);
  326.  
  327.   { Set up the display context to draw the real image }  
  328.   SetROP2(DC, r2_CopyPen);
  329.   SelectObject(DC, Pen);
  330.   if Filled then 
  331.     SelectObject(DC, Brush)
  332.   else
  333.     SelectObject(State^.MemDC, GetStockObject(Null_Brush));
  334.  
  335.   { Draw the actual image }
  336.   DrawObject(DC);
  337.   with State^ do
  338.   begin
  339.     X1 := X1 + Offset.X;
  340.     Y1 := Y1 + Offset.Y;
  341.     X2 := X2 + Offset.X;
  342.     Y2 := Y2 + Offset.Y;
  343.   end;
  344.   DrawObject(State^.MemDC);
  345. end;
  346.  
  347. { Allow the real tool to specify the image it draws.
  348. }
  349. procedure TBoxTool.DrawObject(aDC: HDC);
  350. begin
  351. end;
  352.  
  353. { TRectTool }
  354.  
  355. { Draw a rectangle.
  356. }
  357. procedure TRectTool.DrawObject(aDC: HDC);
  358. begin
  359.   Rectangle(aDC, X1, Y1, X2, Y2);
  360. end;
  361.  
  362. { TSelectTool }
  363.  
  364. { Creation }
  365. constructor TSelectTool.Init(AState: PState; IconName, CursorName: PChar;
  366.   AFilled: Boolean);
  367. begin
  368.   TRectTool.Init(AState, IconName, CursorName, AFilled);
  369.   SelectionDC := 0;
  370. end;
  371.  
  372. { Utility }
  373. { Make sure there is no active selection before exiting. If there is an image
  374.   in the selection paste it into the current image.
  375. }
  376. procedure TSelectTool.Deselect;
  377. begin
  378.   DropSelection;
  379. end;
  380.  
  381. { Actual drawing }
  382. { The selection tool has two states of operation: While the selection is
  383.   being made, it operates as a rectangle tool. If a selection has been made
  384.   and the mouse clicks on it, the selection is dragged with the mouse.
  385.  
  386.   SelectionDC is valid only during dragging and thus serves as the
  387.   flag to distinguish the two modes during drawing.
  388.  
  389.   Dragging the selection is effected by creating a copy (i.e., a
  390.   bitmap) of the selection and alternately restoring the screen to the
  391.   original (actually, only restoring those pieces that are revealed by
  392.   moving the selection), and copying the selection bitmap to the screen.
  393.  
  394.   Throughout dragging
  395.     X1, Y1 contains the previous mouse position
  396.     State^.Selection contains the current coordinates of the selection
  397. }
  398. procedure TSelectTool.DrawBegin(X, Y: Integer);
  399. var
  400.   Pt: TPoint;
  401. begin
  402.   { Check to see if there is a hit on the selection }
  403.   Pt.X := X;
  404.   Pt.Y := Y;
  405.   if PtInRect(State^.Selection, Pt) then
  406.     { Drag selection }
  407.   begin
  408.     { Last mouse position }
  409.     X1 := X;
  410.     Y1 := Y;
  411.  
  412.     { Create the selection bitmap if necessary. (It may already have been
  413.       created, for example through a Paste operation.) }
  414.     if State^.SelectionBM = 0 then
  415.       with State^.Selection, State^ do
  416.       begin
  417.     PickUpSelection(MemDC, Left + Offset.X, Top + Offset.Y,
  418.       Right-Left, Bottom-Top);
  419.  
  420.         { The convention is to cut the selection, so white out
  421.           the hole }
  422.     PatBlt(MemDC, Left + Offset.X, Top + Offset.Y,
  423.       Right - Left, Bottom - Top, Whiteness);
  424.       end;
  425.  
  426.     { Set up the selection display context }
  427.     SelectionDC := CreateCompatibleDC(DC);
  428.     State^.SelectionBM := SelectObject(SelectionDC, State^.SelectionBM);
  429.   end
  430.   else
  431.   { Make new selection }
  432.   begin
  433.     { Paste down the old one if there is one }
  434.     DropSelection;
  435.     TRectTool.DrawBegin(X, Y);
  436.   end;
  437. end;
  438.  
  439. procedure TSelectTool.DrawTo(X, Y: Integer);
  440. var
  441.   I, Count: Integer;        { Number of rectangles that must be restored }
  442.   MoveX, MoveY: Integer;    { Change in X, Y coordinates of selection }
  443.   Result: RectArray;        { Rectangles that must be restored }
  444.   NewCoords: TRect;        { The new coordinates of selection }
  445. begin
  446.   if SelectionDC <> 0 then    { Dragging }
  447.   begin
  448.  
  449.     { Figure out the new coordinates }
  450.     MoveX := X - X1;
  451.     MoveY := Y - Y1;
  452.     with State^.Selection do
  453.       SetRect(NewCoords, Left + MoveX, Top + MoveY, Right + MoveX,
  454.         Bottom + MoveY);
  455.  
  456.     { Determine the area that must be repainted. Note that this will always
  457.       be 0, 1, or 2 rectangles exactly }
  458.     Count := SubtractRect(Result, State^.Selection, NewCoords);
  459.  
  460.     { Repaint the rectangles revealed by the move }
  461.     for I := 0 to Count-1 do
  462.       with Result[I], State^ do
  463.     BitBlt(DC, Left, Top, Right - Left, Bottom - Top,
  464.       State^.MemDC, Left + Offset.X, Top + Offset.Y, SrcCopy);
  465.  
  466.     { Update and repaint the selection }
  467.     with NewCoords do
  468.       SetRect(State^.Selection, Left, Top, Right, Bottom);
  469.     X1 := X;
  470.     Y1 := Y;
  471.     DrawObject(DC);
  472.   end
  473.   else                { Selecting }
  474.     TRectTool.DrawTo(X, Y);
  475. end;
  476.  
  477. procedure TSelectTool.DrawEnd;
  478.  
  479.   procedure Sort(var N1, N2: Integer);
  480.   var
  481.     Temp: Integer;
  482.   begin
  483.     if N1 > N2 then
  484.     begin
  485.       Temp := N1;
  486.       N1 := N2;
  487.       N2 := Temp;
  488.     end;
  489.   end;
  490.  
  491. begin
  492.   DrawObject(DC);
  493.   if SelectionDC <> 0 then
  494.   begin
  495.     { Clean up }
  496.     State^.SelectionBM := SelectObject(SelectionDC, State^.SelectionBM);
  497.     DeleteDC(SelectionDC);
  498.     SelectionDC := 0;
  499.   end
  500.   else
  501.   begin
  502.     { Update the selection }
  503.     Sort(X1, X2);
  504.     Sort(Y1, Y2);
  505.     SetRect(State^.Selection, X1, Y1, X2, Y2);
  506.   end;
  507. end;
  508.  
  509. procedure TSelectTool.DrawObject(aDC: HDC);
  510. begin
  511.   if SelectionDC <> 0 then
  512.     { Draw the selection bitmap }
  513.     with State^.Selection, State^ do
  514.       BitBlt(aDC, Left, Top, Right-Left, Bottom-Top,
  515.     SelectionDC, 0, 0, SrcCopy)
  516.   else
  517.     { Pretend to be a rectangle }
  518.     TRectTool.DrawObject(aDC)
  519. end;
  520.  
  521. { Utilities }
  522. { Set the selection bitmap to be a bitmap that contains a copy of the
  523.   bits contained in the indicated rectangle of the bitmap in a drawing
  524.   context.
  525. }
  526. procedure TSelectTool.PickUpSelection(aDC: HDC; Left, Top, Width,
  527.   Height: Integer);
  528. var
  529.   SelDC: HDC;            { For copying into the selection bitmap }
  530. begin
  531.   { Paste down the current selection if there is one }
  532.   if State^.SelectionBM <> 0 then DropSelection;
  533.  
  534.   { Set the default screen coordinates for the selection if necessary }
  535.   if IsRectEmpty(State^.Selection) then 
  536.     SetRect(State^.Selection, 0, 0, Width, Height);
  537.   
  538.   { Create the selection bitmap and copy the bits }
  539.   SelDC := CreateCompatibleDC(aDC);
  540.   State^.SelectionBM := CreateCompatibleBitmap(aDC, Width, Height);
  541.   State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  542.   BitBlt(SelDC, 0, 0, Width, Height, aDC, Left, Top, SrcCopy);
  543.  
  544.   { Clean up }
  545.   State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  546.   DeleteDC(SelDC);
  547. end;
  548.  
  549. { Set the current selection to none without copying back the selection bitmap.
  550. }
  551. procedure TSelectTool.ReleaseSelection;
  552. begin
  553.   if not IsRectEmpty(State^.Selection) then
  554.   begin
  555.     InvalidateRect(Window, @State^.Selection, False);
  556.     SetRectEmpty(State^.Selection);
  557.     if State^.SelectionBM <> 0 then
  558.     begin
  559.       DeleteObject(State^.SelectionBM);
  560.       State^.SelectionBM := 0;
  561.     end;
  562.   end;
  563. end;
  564.  
  565. { Set the current selection to none, but paste the selection bitmap down.
  566. }
  567. procedure TSelectTool.DropSelection;
  568. var
  569.   SelDC: HDC;
  570. begin
  571.   if State^.SelectionBM <> 0 then
  572.   begin
  573.     { Mark the bitmap as having been modified }
  574.     State^.IsDirtyBitmap := True;
  575.  
  576.     { Copy the selection bitmap back }
  577.     SelDC := CreateCompatibleDCW(Window);
  578.     State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  579.     with State^.Selection, State^ do
  580.       BitBlt(MemDC, Left + Offset.X, Top + Offset.Y,
  581.         Right + Offset.X, Bottom + Offset.Y, SelDC, 0, 0, SrcCopy);
  582.     State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  583.  
  584.     { Clean up }
  585.     DeleteDC(SelDC);
  586.   end;
  587.   ReleaseSelection;
  588. end;
  589.  
  590. { TEllipseTool }
  591.  
  592. { Draw an ellipse.
  593. }
  594. procedure TEllipseTool.DrawObject(aDC: HDC);
  595. begin
  596.   Ellipse(aDC, X1, Y1, X2, Y2);
  597. end;
  598.  
  599. { TLineTool }
  600.  
  601. { Actual drawing }
  602. procedure TLineTool.DrawObject(aDC: HDC);
  603. begin
  604.   MoveTo(aDC, X1, Y1);
  605.   LineTo(aDC, X2, Y2);
  606. end;
  607.  
  608. end.
  609.